package br.com.felix.fwt.util;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

import br.com.felix.fwt.log.LoggerFactory;
import br.com.felix.fwt.servlet.FelixWebToolkitServlet;
import br.com.felix.fwt.ui.css.Align;
import br.com.felix.fwt.ui.css.PixelSize;
import br.com.felix.fwt.ui.css.Style;
import br.com.felix.fwt.ui.exception.FontUnavailableException;
import br.com.felix.fwt.util.exception.ImageGenerationException;

/**
 * Generates transparent images with text.
 * */
public class TextImageBuilder {

	private static Logger logger = LoggerFactory.getLogger(TextImageBuilder.class);

	/**
     * Folder where images will be stored.
     * */
    private static String imagesFolder = "/images/";

    /**
     * Folder where fonts will be stored.
     * */
    private static String fontsFolder = "/fonts/";
    
    /**
     * Fonts that are available in the system.
     * */
    private static Set<String> fontSet;
    
    /**
     * Fonts that are available in fontsFolder.
     * */
    private static Map<String, Font> myFontMap;
    
    static{
        GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Font[] fonts = e.getAllFonts();
        fontSet = new HashSet<String>();
        for (Font f : fonts) {
          fontSet.add(f.getName());
        }
        
        myFontMap = new HashMap<String, Font>();
    }
	
	private Integer width;
	private Integer height;
	private String text;
	private Font font;
	private Color color;
	private Align textAlign;
	
    
    public static synchronized String getImagesFolder() {
        return imagesFolder;
    }

    public static synchronized void setImagesFolder(String imagesFolder) {
        TextImageBuilder.imagesFolder = imagesFolder;
    }
	
	
	private String getFilePath(){
	    return imagesFolder + getName(text);
	}
	
	/**
     * Returns the path to be used on the 'src' property of an html image.
     * */
	public String getWebFilePath(){
	    return ".." + getFilePath();
	}
	
	
	/**
	 * Tells whether the file has already been generated with the name convention.
	 * */
	public boolean needsToBeCreated(){
        String realPath = getRealFilePath();
        File file = new File(realPath);
        return !file.exists();
	}

	/**
	 * Returns the file path on the file system.
	 * */
    private String getRealFilePath() {
        return FelixWebToolkitServlet.getFwtContext().request.getServletContext().getRealPath(getFilePath());
    }
	
	/**
	 * Shortcut to define the properties using Style.
	 * @throws FontUnavailableException 
	 * */
	public void setStyle(Style style) throws FontUnavailableException{
	    if (!(style.height instanceof PixelSize) || !(style.width instanceof PixelSize)){
            String msg = "Could not generate image. Size of TextImageButton must be specified using PixelSize.";
            logger.error(msg);
            throw new IllegalArgumentException(msg);
        }
	    setTextAlign(style.textAlign);
        setColor(style.color);
        setFont(getFont(style));
        setWidth(((PixelSize)style.width).value);
        setHeight(((PixelSize)style.height).value);
        
	}
	
	
	
	/**
	 * If font is not available on system, try to get it from {@link #fontsFolder}.
	 * @throws FontUnavailableException when font is not available neither from the system nor from the {@link #fontsFolder}.
	 * */
	private Font getFont(Style style) throws FontUnavailableException {
        String fontName = style.fontFamily;
        if (!fontSet.contains(fontName)){
            Font font = myFontMap.get(fontName);
            if (font == null){
                String realPath = FelixWebToolkitServlet.getFwtContext().request.getServletContext().getRealPath(fontsFolder + fontName + ".ttf");
                logger.info("Obtaining font from file '" + realPath +"'.");
                try {
                    font = Font.createFont(Font.PLAIN, new File(realPath));
                    myFontMap.put(fontName, font);
                }
                catch (FontFormatException e) {
                    String msg = "Could not create font from file: a font format exception ocurred.";
                    logger.error(msg, e);
                    throw new FontUnavailableException(msg, e);
                }
                catch (IOException e) {
                    String msg = "Could not create font from file: an IO exception ocurred.";
                    logger.error(msg, e);
                    throw new FontUnavailableException(msg, e);
                }
            }
            return font.deriveFont((float)style.fontSize.getPointSize());
        }
        return new Font(fontName, Font.PLAIN, style.fontSize.getPointSize());
    }
	
	
	/**
	 * Prefix used to name files.
	 * */
	protected String getPrefix(){
	    return "img";
	}
	
	/**
     * Generates a camelCase name for the image.
     * */
    protected String getName(String value){
        StringBuilder name = new StringBuilder(getPrefix());
        String[] parts = value.split(" ");
        for (String part: parts){
            name.append(part.substring(0, 1).toUpperCase());
            if(part.length()>1){
                name.append(part.substring(1, part.length()).toLowerCase());
            }
        }
        name.append(".png");
        return name.toString();
    }
	
	public Align getTextAlign() {
		return textAlign;
	}

	public TextImageBuilder setTextAlign(Align align) {
		this.textAlign = align;
		return this;
	}

	public Color getColor() {
		return color;
	}

	public TextImageBuilder setColor(Color color) {
		this.color = color;
		return this;
	}
	
	public TextImageBuilder setColor(br.com.felix.fwt.ui.css.Color color) {
		this.color = new java.awt.Color(color.getRed(), color.getGreen(), color.getBlue());
		return this;
	}

	public Font getFont() {
		return font;
	}

	public TextImageBuilder setFont(Font font) {
		this.font = font;
		return this;
	}

	public Integer getWidth() {
		return width;
	}

	public TextImageBuilder setWidth(Integer width) {
		this.width = width;
		return this;
	}

	public Integer getHeight() {
		return height;
	}

	public TextImageBuilder setHeight(Integer height) {
		this.height = height;
		return this;
	}

	public String getText() {
		return text;
	}

	public TextImageBuilder setText(String text) {
		this.text = text;
		return this;
	}

	
	
	public static void main (String args[]){
		try {
			TextImageBuilder img = new TextImageBuilder(){
                @Override
                public Point getTextPosition(Graphics2D g) {
                    Point p = super.getTextPosition(g);
                    p.y = 20;
                    return p;
                }
			};
			//Font font = new Font("Times new Roman", Font.PLAIN, 20);
			Font font = Font.createFont(Font.PLAIN, new File("D:/Padoa/workspace/StephCharlie/WebContent/fonts/Jellyka Western Princess.ttf"));
			//Font font = Font.createFont(Font.PLAIN, new File("D:/Padoa/workspace/StephCharlie/WebContent/fonts/Heineken.ttf"));
			font = font.deriveFont(20f);
			img.setWidth(360).setHeight(30).setFont(font).setText("Lista de Presentes").setColor(Color.black);
			img.makeImage(new File("d:/Padoa/test.png"));
			System.out.println("Success!");
		} 
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	
	/**
	 * Generates image using name and location conventions. 
	 * */
	public void makeImage() throws ImageGenerationException{
	    makeImage(new File(getRealFilePath()));
	}
	
	
	public Point getTextPosition(Graphics2D g){
        TextLayout tl = new TextLayout(text, font, g.getFontRenderContext());
        Rectangle rect = tl.getBounds().getBounds();
        int x = 0, y = 0;
        int textHeight = rect.height;
        int textWidth = rect.width;
        if (textAlign==null) {
            textAlign = Align.CENTER;
        }
        switch (textAlign) {
            case LEFT:
            case RIGHT:
            case CENTER:
                int diffHeight = height - textHeight;
                int diffWidth = width - textWidth;
                x = diffWidth / 2;
                y = height - (diffHeight  / 2);
        }
        return new Point(x, y);
	}
	
	/**
	 * @throws ImageGenerationException 
	 * 
	 * */
	public void makeImage(File dest) throws ImageGenerationException {
		BufferedImage buffimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = buffimg.createGraphics();
		Point p = getTextPosition(g);
		g.setFont(font);
		g.setColor(color);
		if (p.y > height || p.y < 0) {
		    String msg = "Font size overlaps image size.";
		    throw new ImageGenerationException(msg);
		}
		g.drawChars(text.toCharArray(), 0, text.length(), p.x, p.y);
		g.dispose();
		try {
			ImageIO.write(buffimg, "png", dest);
		} 
		catch (IOException e) {
			String msg = "IO error while writing generated image.";
			logger.error(msg, e);
			throw new ImageGenerationException(msg, e);
		}
	}

	
	/**
	 * Returns an image where colors from range c1 to c2 are made translucent.
	 * */
	private static Image transformColorToTransparency(BufferedImage image, Color c1, Color c2) {
		// Primitive test, just an example
		final int r1 = c1.getRed();
		final int g1 = c1.getGreen();
		final int b1 = c1.getBlue();
		final int r2 = c2.getRed();
		final int g2 = c2.getGreen();
		final int b2 = c2.getBlue();
		ImageFilter filter = new RGBImageFilter() {
			public final int filterRGB(int x, int y, int rgb) {
				int r = (rgb & 0xFF0000) >> 16;
				int g = (rgb & 0xFF00) >> 8;
				int b = rgb & 0xFF;
				if (r >= r1 && r <= r2 && g >= g1 && g <= g2 && b >= b1 && b <= b2) {
					// Set fully transparent but keep color
					return rgb & 0xFFFFFF;
				}
				return rgb;
			}
		};

		ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
		return Toolkit.getDefaultToolkit().createImage(ip);
	}
	
	
	private static BufferedImage imageToBufferedImage(Image img, int width, int height){
		BufferedImage transpBuffImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2d = transpBuffImage.createGraphics();
		g2d.drawImage(img, 0, 0, null);
		g2d.dispose();
		return transpBuffImage;
	}

}
